package com.seed.core.query;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryCondition;
import com.mybatisflex.core.query.QueryTable;
import com.mybatisflex.core.query.QueryWrapper;
import com.seed.core.enumeration.Direction;
import com.seed.core.pojo.Join;
import com.seed.core.pojo.OrderBy;
import com.seed.core.pojo.seed.Seed;
import com.seed.core.query.pojo.Declared;
import com.seed.core.query.pojo.Ordered;
import com.seed.core.query.pojo.Searched;
import com.seed.util.SeedKit;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class SeedQuery {
    private final Seed seed;
    private final Map<String, Declared> declareds;

    private final SeedDeclared seedDeclared;
    private SeedSearched seedSearched;
    private SeedKeyword seedKeyword;
    private SeedOrdered seedOrdered;
    private SeedDataPermied seedDataPermied;

    public SeedQuery(Seed seed, Map<String, Declared> declareds) {
        this.seed = seed;
        this.declareds = declareds;
        this.seedDeclared = new SeedDeclared(seed, declareds);
    }

    public SeedQuery(Seed seed, Map<String, Declared> declareds, Map<String, Searched> searcheds, Map<String, Ordered> ordereds) {
        this(seed, declareds);
        if(searcheds != null) this.seedSearched = new SeedSearched(seed, searcheds);
        if(ordereds != null) this.seedOrdered = new SeedOrdered(seed, ordereds);
    }

    public QueryWrapper getQueryWrapper() {
        QueryTable table = new QueryTable(seed.getTableName()).as("t");
        QueryWrapper qw = QueryWrapper.create().from(table);

        Map<String, QueryColumn> selects = seedDeclared.getSelects();
        if(selects.isEmpty()) {
            QueryColumn[] columns = SeedKit.getDefaultDeclares(seed)
                .stream()
                .map(columnName -> new QueryColumn(table, columnName))
                .toList()
                .toArray(new QueryColumn[0]);
            qw.select(columns);
        } else {
            qw.select(selects.values().toArray(new QueryColumn[0]));
        }

        Map<String, Join> joins = new HashMap<>();
        for(Map.Entry<String, Join> entry: seedDeclared.getLeftJoins().entrySet()) {
            joins.putIfAbsent(entry.getKey(), entry.getValue());
        }
        if(seedSearched != null) {
            for(Map.Entry<String, Join> entry: seedSearched.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(seedKeyword != null) {
            for(Map.Entry<String, Join> entry: seedKeyword.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(seedDataPermied != null) {
            for(Map.Entry<String, Join> entry: seedDataPermied.getJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }
        if(seedOrdered != null) {
            for(Map.Entry<String, Join> entry: seedOrdered.getLeftJoins().entrySet()) {
                joins.putIfAbsent(entry.getKey(), entry.getValue());
            }
        }

        for(Join leftJoin: joins.values()) {
            qw.leftJoin(leftJoin.getTable()).on(leftJoin.getOn());
        }

        Map<String, SeedInCondition> inQuerys = new LinkedHashMap<>();
        if(seedSearched != null) inQuerys.putAll(seedSearched.getInQuerys());
        if(seedKeyword != null) {
            for(Map.Entry<String, SeedInCondition> entry: seedKeyword.getInQuerys().entrySet()) {
                String key = entry.getKey();
                SeedInCondition value = entry.getValue();
                SeedInCondition seedInCondition = inQuerys.get(key);
                if(seedInCondition == null) {
                    inQuerys.put(key, value);
                } else {
                    seedInCondition.setDeclared(value.getDeclared());
                    seedInCondition.setKeyword(seedKeyword.getKeyword());
                }
            }
        }
        if(seedDataPermied != null) {
            for(Map.Entry<String, SeedInCondition> entry: seedDataPermied.getInQuerys().entrySet()) {
                String key = entry.getKey();
                SeedInCondition value = entry.getValue();
                SeedInCondition seedInCondition = inQuerys.get(key);
                if(seedInCondition == null) {
                    inQuerys.put(key, value);
                } else {
                    seedInCondition.setDataPermied(value.getDataPermied());
                }
            }
        }

        for(Map.Entry<String, SeedInCondition> entry: inQuerys.entrySet()) {
            qw.and(entry.getValue().getQueryCondition());
        }

        if(seedSearched != null) {
            for(QueryCondition condition: seedSearched.getWheres()) qw.and(condition);
        }

        if(seedKeyword != null) {
            qw.and(queryWrapper -> {
                for(QueryCondition condition: seedKeyword.getWheres()) queryWrapper.or(condition);
            });
        }

        if(seedDataPermied != null) qw.and(seedDataPermied.getQueryCondition());
        if(seedOrdered != null) {
            for(OrderBy orderBy: seedOrdered.getOrders().values()) {
                qw.orderBy(orderBy.getColumn(), orderBy.getDirection().equals(Direction.asc));
            }
        }

        return qw;
    }

    public void fills(Object data) {
        fills(List.of(data));
    }

    public void fills(List<?> rootList) {
        for(Map.Entry<String, SeedFill> entry: seedDeclared.getFills().entrySet()) {
            String key = entry.getKey();
            SeedFill seedFill = entry.getValue();
            if(seedSearched != null) {
                Searched searched = seedSearched.getFillSearched(key);
                if(searched != null) seedFill.setSearched(searched);
            }
            if(seedKeyword != null) {
                String keyword = seedKeyword.getKeyword();
                Declared declared = seedKeyword.getFillKeyworded(key);
                if(StrUtil.isNotBlank(keyword) && declared != null) {
                    seedFill.setKeyword(keyword, declared);
                }
            }
            if(seedDataPermied != null) {
                List<List<List<Map<String, Searched>>>> dataPermied = seedDataPermied.getFillDataPermied();
                if(dataPermied != null) seedFill.setDataPermied(dataPermied);
            }
            if(seedOrdered != null) {
                Ordered ordered = seedOrdered.getFillOrdered(key);
                if(ordered != null) seedFill.setOrdered(ordered);
            }

            entry.getValue().fill(rootList);
        }
    }

    public void setKeyword(String keyword) {
        if(StrUtil.isNotBlank(keyword)) this.seedKeyword = new SeedKeyword(keyword, seed, declareds);
    }

    public void setDataPermieds(List<List<List<Map<String, Searched>>>> dataPermieds) {
        if(dataPermieds != null) this.seedDataPermied = new SeedDataPermied(seed, dataPermieds);
    }
}
